面向对象 - 对象的相等判断
面向对象 - 对象的相等判断
比较值: is 和 ==
is 比较的是两个实例对象内存地址是否一样,比较的是两个对象的内容是否相等,即内存地址可以不一样,内容一样就可以了,其实从严格意义上来说,**两个对象进行对比的规则,是对象的类的 __eq__
方法定义的**,我们可以通过重写类的 __eq__
来自定义通过 == 时的行为,这叫运算符重载。
官方文档中说 is 表示的是对象标示符(object identity),而 == 表示的是相等(equality)。is 的作用是用来检查对象的标示符是否一致,也就是比较两个对象在内存中的地址是否一样,而 == 是用来检查两个对象是否相等。
我们在检查 a is b 的时候,其实相当于检查 id(a) == id(b)。而检查 a == b 的时候,实际是调用了对象 a 的
__eq()__
方法,a == b 相当于 a.__eq__
(b)。一般情况下,如果 a is b 返回 True 的话,即 a 和 b 指向同一块内存地址的话,a == b 也返回 True,即 a 和 b 的值也相等。
内置类型均已重载 __eq__
所以可以实现根据对象内容比较,自定义类需要自行重写 __eq__
来实现通过 == 比较时根据内容判断是否相等,如果没有重 __eq__
,那么在比较自定义类的两个实例的时候会一直返回 False
在 Java 中 == 用于比较对象内存,而 equals 方法比较对象内容,只不过 Java 无法实现运算符重载,不能自定 == 行为,不然也不需要 equals 方法了
运算符重载,确实是非常方便的东西。
对于数字、字符串、bool 等基本数据类型,他们都属于不可变对象,在 Pycharm 中,对这些对象进行 is
和 ==
的结果都是相同的,即这些对象都是单例对象,也就是同一个值在内存中只会有一个对象,甚至 None is None
也是 True。
在《函数.md》的
可更改(mutable)与不可更改(immutable)对象
小节中,我们讨论过这个概念
为什么,这是因为 Python 的性能优化机制:Python 的小整数池和字符串驻留 (intern) 机制在起作用,这些机制在不同的场景下有不同的表现:
参考博客:【Python】浅谈 小整数池 + 字符串驻留 (Intern 机制)_何处闻韶的博客-CSDN博客
我实验的 Python 当前版本为
3.10.4
,跟博客中的情况有所出入,看博客一定要自己实践,情况可能不一样
-
交互式执行也就是在 shell 中执行
-
整数:(分两行)给两个变量赋值,且赋相同的值,那么只要这个值在 [-5, 256] 这个范围内,这两个变量指向的就是同一个对象,内存地址相同,不在这个范围内,虽然值相同,这两个变量指向的也是两个对象;两个变量赋值书写在同一行、那么就没这个数值范围限制,只要这个两个变量的值相同,那这两个变量指向的始终是同一个对象
-
字符串:分两行)给两个变量赋值,且赋相同的字符串,如果字符串仅由由字母、数字和下划线构成(不包括空格),那么这两个变量指向的就是同一个对象,如果字符串不符合这个条件,虽然值相同,这两个变量指向的也是两个对象;两个变量赋值书写在同一行、那么就没这个限制,只要这个两个变量的值相同,那这两个变量指向的始终是同一个对象
-
-
在 Pycharm 中执行,同一个模块的所有值相同的不可变对象(数字和字符串),指向的都是同一个对象
简单实践如下:
# is 和 ==
# ==比较的是两个对象的内容是否相等,即内存地址可以不一样,内容一样就可以了;而is比较的是两个实例对象内存地址是否一样。
# 我们可以通过重写类的 __eq__ 来自定义通过 == 时的行为,这叫运算符重载,内置类型均已重载 __eq__ 所以可以实现 根据对象内容比较,自定义类需要自行重写 __eq__ 来实现通过 == 比较时根据内容判断是否相等
# 在 Java 中 == 用于比较对象内存,而equals方法比较对象内容,只不过Java无法实现运算符重载,不能自定 == 行为,不然也不需要equals方法了
print("-----------数字----------------")
# 数字
a = 1245678985
b = int(1245678985)
# True
print(a is b)
# True
print(a == b)
print("-----------小数----------------")
# 小数
a = 12456.78985
b = float(12456.78985)
# True
print(a is b)
# True
print(a == b)
print("-----------字符串----------------")
# 字符串
a = "1245678985"
b = str("1245678985")
# True
print(a is b)
# True
print(a == b)
print("-----------长字符串----------------")
# 长字符串
a = "hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world "
b = "hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world "
# True
print(a is b)
# True
print(a == b)
print("-----------bool----------------")
# bool
# True
print(True is True)
# True
print(True == True)
print("-----------列表----------------")
# 列表
a = ["12", 45, True]
b = ["12", 45, True]
# False
print(a is b)
# True
print(a == b)
print("-----------集合----------------")
# 集合
a = {12, 78, "aaa"}
b = {"aaa", 78, 12}
# False
print(a is b)
# True
print(a == b)
print("-----------None----------------")
# None
# False
print(None is not None)
# True
print(None is None)
print("-----------自定义类----------------")
class test_value_equal:
def __init__(self, name: str, age: int):
self.name: str = name
self.age: int = age
# 如果不重写 __eq__, a == b 会返回false
def __eq__(self, other):
return self.name == other.name and self.age == other.age
a = test_value_equal("xiashuo.xyz", 12)
b = test_value_equal("xiashuo.xyz", 12)
# False
print(a is b)
# 需要重写 __eq__ 方法,否则返回false。
# True
print(a == b)
print("-----------跨方法----------------")
# 跨调用栈
a = "xiashuo.xyz"
def test_value():
b = "xiashuo.xyz"
# True
print(a is b)
# True
print(a == b)
test_value()
print("-----------跨类----------------")
# 跨类
class test_obj:
def __init__(self):
self.name = "xiashuo.xyz"
obj = test_obj()
# True
print(a is obj.name)
# True
print(a == obj.name)
print("-----------跨模块----------------")
from test_value import c
a = "xiashuo.xyz"
# False
print(a is c)
# True
print(a == c)
输出:
-----------数字----------------
True
True
-----------小数----------------
True
True
-----------字符串----------------
True
True
-----------长字符串----------------
True
True
-----------bool----------------
True
True
-----------列表----------------
False
True
-----------集合----------------
False
True
-----------None----------------
False
True
-----------自定义类----------------
False
True
-----------跨方法----------------
True
True
-----------跨类----------------
True
True
-----------跨模块----------------
False
True
一般来说,应该使用 ==
比较两个对象是否相等,因为这是比较它们的内容是否相同;而 is
比较的是两个对象是否是同一个对象,通常用于判断一个变量是否为 None。
比较类型:tupe 和 isinstance 、 issubclass
type()
方法我们了解很多了,就是返回对象的类型,
isinstance()
返回对象是否是指定类或者指定类的子类的实例,在指定类型的地方可以输入一个元组,比如 isinstance(x, (A, B))
,相当于 isinstance(x, A) or isinstance(x, B)
。
issubclass()
返回类型是否是指定类型的子类。自定类型是自身类型也会返回 true。在指定类型的地方可以输入一个元组,比如 issubclass(x, (A, B))
,相当于 issubclass(x, A) or issubclass(x, B)
。
尽管 isinstance() 很灵活,但它没有执行 " 严格匹配 " 比较, 如果 obj 是一个给定类型的实例或者子类的实例,也会返回 True.
如果想进行严格匹配,仍需要type(obj) is xxx
进行判断,不建议使用type(obj) == xxx
简单实践如下:
# type isinstance
class base_example:
pass
class type_checke_example(base_example):
def __init__(self):
self.name: str = "xiashuo.xyz"
obj = type_checke_example()
# <class '__main__.type_checke_example'>
print(type(obj))
# 检查两个对象是否是同一个类型
obj2 = type_checke_example()
# 用 is 比较合适 不要用 == ,因为类型对象也是唯一的
# True
print(type(obj) is type(obj2))
# True
print(type(obj) is type_checke_example)
# 类型对象也是唯一的
# True
print(type(123) is int)
# 检查一个实例是否是一个类的实例
is_class = isinstance(obj, type_checke_example)
# True
print(is_class)
# 这个实例是指定类型的子类的子类的实例,也会返回true
is_class = isinstance(obj, base_example)
# True
print(is_class)
# None 也是 对象
# True
print(isinstance(None, object))
# 父子类型检查
# False
print(issubclass(type_checke_example, int))
# True
print(issubclass(type_checke_example, base_example))
# True
print(issubclass(type_checke_example, type_checke_example))
输出
<class '__main__.type_checke_example'>
True
True
True
True
True
True
False
True
True